/**
 * @Author: 时洋
 * @Last Modified by: shiyang
 * @Last Modified time: 2019-04-28 14:58:16
 */
export const forEach = (arr, fn) => {
    if (!arr.length || !fn) return
    let i = -1
    let len = arr.length
    while (++i < len) {
        let item = arr[i]
        fn(item, i, arr)
    }
}

/**
 * @param {Array} arr1
 * @param {Array} arr2
 * @description 得到两个数组的交集, 两个数组的元素为数值或字符串
 */
export const getIntersection = (arr1, arr2) => {
    let len = Math.min(arr1.length, arr2.length)
    let i = -1
    let res = []
    while (++i < len) {
        const item = arr2[i]
        if (arr1.indexOf(item) > -1) res.push(item)
    }
    return res
}

/**
 * @param {Array} arr1
 * @param {Array} arr2
 * @description 得到两个数组的并集, 两个数组的元素为数值或字符串
 */
export const getUnion = (arr1, arr2) => {
    return Array.from(new Set([ ...arr1, ...arr2 ]))
}

/**
 * @param {Array} target 目标数组
 * @param {Array} arr 需要查询的数组
 * @description 判断要查询的数组是否至少有一个元素包含在目标数组中
 */
export const hasOneOf = (target, arr) => {
    return target.some((_) => arr.indexOf(_) > -1)
}

/**
 * @param {String|Number} value 要验证的字符串或数值
 * @param {*} validList 用来验证的列表
 */
export function oneOf (value, validList) {
    for (let i = 0; i < validList.length; i++) {
        if (value === validList[i]) {
            return true
        }
    }
    return false
}

/**
 * @param {Number} timeStamp 判断时间戳格式是否是毫秒
 * @returns {Boolean}
 */
const isMillisecond = (timeStamp) => {
    const timeStr = String(timeStamp)
    return timeStr.length > 10
}

/**
 * @param {Number} timeStamp 传入的时间戳
 * @param {Number} currentTime 当前时间时间戳
 * @returns {Boolean} 传入的时间戳是否早于当前时间戳
 */
const isEarly = (timeStamp, currentTime) => {
    return timeStamp < currentTime
}

/**
 * @param {Number} num 数值
 * @returns {String} 处理后的字符串
 * @description 如果传入的数值小于10，即位数只有1位，则在前面补充0
 */
const getHandledValue = (num) => {
    return num < 10 ? '0' + num : num
}

/**
 * @param {Number} timeStamp 传入的时间戳
 * @param {Number} startType 要返回的时间字符串的格式类型，传入'year'则返回年开头的完整时间
 */
const getDate = (timeStamp, startType) => {
    const d = new Date(timeStamp * 1000)
    const year = d.getFullYear()
    const month = getHandledValue(d.getMonth() + 1)
    const date = getHandledValue(d.getDate())
    const hours = getHandledValue(d.getHours())
    const minutes = getHandledValue(d.getMinutes())
    const second = getHandledValue(d.getSeconds())
    let resStr = ''
    if (startType === 'year') resStr = year + '-' + month + '-' + date + ' ' + hours + ':' + minutes + ':' + second
    else resStr = month + '-' + date + ' ' + hours + ':' + minutes
    return resStr
}
/**
 * @param {String} time 传入的时间
 * @param {Boolen} isSecond 传入的时间是秒还是毫秒,传入true代表传入的时间为秒
 * @param {Boolen} type 要返回的时间字符串的格式类型,传入true则返回带时分秒的完整时间
 */
export const formatDate = (time, isSecond, type) => {
    let d = isSecond ? new Date(time * 1000) : new Date(time)
    let year = d.getFullYear()
    let month = getHandledValue(d.getMonth() + 1)
    let date = getHandledValue(d.getDate())
    let hours = getHandledValue(d.getHours())
    let minutes = getHandledValue(d.getMinutes())
    let second = getHandledValue(d.getSeconds())
    let resStr = ''
    if (type) {
        resStr = year + '-' + month + '-' + date + ' ' + hours + ':' + minutes + ':' + second
    } else {
        resStr = year + '-' + month + '-' + date
    }
    return resStr
}

/**
 * @param {String|Number} timeStamp 时间戳
 * @returns {String} 相对时间字符串
 */
export const getRelativeTime = (timeStamp) => {
    // 判断当前传入的时间戳是秒格式还是毫秒
    const IS_MILLISECOND = isMillisecond(timeStamp)
    // 如果是毫秒格式则转为秒格式
    if (IS_MILLISECOND) Math.floor((timeStamp /= 1000))
    // 传入的时间戳可以是数值或字符串类型，这里统一转为数值类型
    timeStamp = Number(timeStamp)
    // 获取当前时间时间戳
    const currentTime = Math.floor(Date.parse(new Date()) / 1000)
    // 判断传入时间戳是否早于当前时间戳
    const IS_EARLY = isEarly(timeStamp, currentTime)
    // 获取两个时间戳差值
    let diff = currentTime - timeStamp
    // 如果IS_EARLY为false则差值取反
    if (!IS_EARLY) diff = -diff
    let resStr = ''
    const dirStr = IS_EARLY ? '前' : '后'
    // 少于等于59秒
    if (diff <= 59) resStr = diff + '秒' + dirStr
    else if (diff > 59 && diff <= 3599) {
        // 多于59秒，少于等于59分钟59秒
        resStr = Math.floor(diff / 60) + '分钟' + dirStr
    } else if (diff > 3599 && diff <= 86399) {
        // 多于59分钟59秒，少于等于23小时59分钟59秒
        resStr = Math.floor(diff / 3600) + '小时' + dirStr
    } else if (diff > 86399 && diff <= 2623859) {
        // 多于23小时59分钟59秒，少于等于29天59分钟59秒
        resStr = Math.floor(diff / 86400) + '天' + dirStr
    } else if (diff > 2623859 && diff <= 31567859 && IS_EARLY) {
        // 多于29天59分钟59秒，少于364天23小时59分钟59秒，且传入的时间戳早于当前
        resStr = getDate(timeStamp)
    } else {
        resStr = getDate(timeStamp, 'year')
    }
    return resStr
}

/**
 * @returns {String} 当前浏览器名称
 */
export const getExplorer = () => {
    const ua = window.navigator.userAgent
    const isExplorer = (exp) => {
        return ua.indexOf(exp) > -1
    }
    const ie11 = (function () {
        return 'ActiveXObject' in window
    })()
    if (isExplorer('MSIE') || ie11) return 'IE'
    else if (isExplorer('Firefox') && !ie11) return 'Firefox'
    else if (isExplorer('Chrome') && !ie11) {
        if (ua.indexOf('Edge') > -1) {
            return 'Edge'
        } else {
            return 'Chrome'
        }
    } else if (isExplorer('Opera') && !ie11) return 'Opera'
    else if (isExplorer('Safari') && !ie11) return 'Safari'
}

/**
 * @description 绑定事件 on(element, event, handler)
 */
export const on = (function () {
    if (document.addEventListener) {
        return function (element, event, handler) {
            if (element && event && handler) {
                element.addEventListener(event, handler, false)
            }
        }
    } else {
        return function (element, event, handler) {
            if (element && event && handler) {
                element.attachEvent('on' + event, handler)
            }
        }
    }
})()

/**
 * @description 解绑事件 off(element, event, handler)
 */
export const off = (function () {
    if (document.removeEventListener) {
        return function (element, event, handler) {
            if (element && event) {
                element.removeEventListener(event, handler, false)
            }
        }
    } else {
        return function (element, event, handler) {
            if (element && event) {
                element.detachEvent('on' + event, handler)
            }
        }
    }
})()

/**
 * 判断一个对象是否存在key，如果传入第二个参数key，则是判断这个obj对象是否存在key这个属性
 * 如果没有传入key这个参数，则判断obj对象是否有键值对
 */
export const hasKey = (obj, key) => {
    if (key) return key in obj
    else {
        let keysArr = Object.keys(obj)
        return keysArr.length
    }
}

/**
 * @param {*} obj1 对象
 * @param {*} obj2 对象
 * @description 判断两个对象是否相等，这两个对象的值只能是数字或字符串
 */
export const objEqual = (obj1, obj2) => {
    const keysArr1 = Object.keys(obj1)
    const keysArr2 = Object.keys(obj2)
    if (keysArr1.length !== keysArr2.length) return false
    else if (keysArr1.length === 0 && keysArr2.length === 0) return true
    else {
        /* eslint-disable-next-line */
        return !keysArr1.some((key) => obj1[key] != obj2[key])
    }
}

/**
 * @description 格式化金额
 * @param number：要格式化的数字
 * @param decimals：保留几位小数 默认0位
 * @param decPoint：小数点符号 默认.
 * @param thousandsSep：千分位符号 默认为,
 */
export const formatMoney = (number, decimals = 0, decPoint = '.', thousandsSep = ',') => {
    number = (number + '').replace(/[^0-9+-Ee.]/g, '')
    let n = !isFinite(+number) ? 0 : +number
    let prec = !isFinite(+decimals) ? 0 : Math.abs(decimals)
    let sep = typeof thousandsSep === 'undefined' ? ',' : thousandsSep
    let dec = typeof decPoint === 'undefined' ? '.' : decPoint
    let s = ''
    let toFixedFix = function (n, prec) {
        let k = Math.pow(10, prec)
        return '' + Math.ceil(n * k) / k
    }
    s = (prec ? toFixedFix(n, prec) : '' + Math.round(n)).split('.')
    let re = /(-?\d+)(\d{3})/
    while (re.test(s[0])) {
        s[0] = s[0].replace(re, '$1' + sep + '$2')
    }
    if ((s[1] || '').length < prec) {
        s[1] = s[1] || ''
        s[1] += new Array(prec - s[1].length + 1).join('0')
    }
    return s.join(dec)
}

/**
 * @description textarea换行符转指定字符
 * @param str：要放到textarea的字符串
 * @param code：要转换成换行的字符,默认为','
 */
export const textareaToString = (str, code = ',') => {
    let lineFeed = '\n'
    let reg = new RegExp(lineFeed, 'g')
    str = str.toString().replace(reg, code)
    return str
}

/**
 * @description 转为textarea换行符
 * @param str：要放到textarea的字符串
 * @param code：要转换成换行的字符,默认为'\n'
 */
export const stringToTextarea = (str, code = '\n') => {
    let reg = new RegExp(code, 'g')
    str = str.replace(reg, '\n')
    return str
}

/**
 * @description 深拷贝
 * @param p：拷贝的对象
 * @param c：拷贝后的对象
 */
export const deepCopy = (p, c = {}) => {
    for (let i in p) {
        if (typeof p[i] === 'object') {
            c[i] = p[i].constructor === Array ? [] : {}
            deepCopy(p[i], c[i])
        } else {
            c[i] = p[i]
        }
    }
    return c
}
/**
 * @description 树型数据转化成级联所要数据
 * @param tree：树数据
 */
export const toCascaderData = (tree, id = 'id', title = 'title') => {
    let arr = []
    if (!!tree && tree.length !== 0) {
        Array.from(tree).forEach((item) => {
            let obj = item
            obj.label = item.title
            obj.value = item.id
            if (item.children && item.children.length !== 0) {
                obj.children = toCascaderData(item.children)
            }
            arr.push(obj)
        })
    }
    return arr
}
